﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Runtime.Tracking;
using Nsf.Samples.RetryActivitySample.Infrastructure.Logger;
using Nsf.Samples.RetryActivitySample.Infrastructure.Utils;

namespace Nsf.Samples.RetryActivitySample.Service.Infrastructure
{
    public class WorkflowRuntimeManager
    {
        #region Internal State

        protected WorkflowRuntime _workflowRuntime;
        protected Log _logger;

        #endregion

        #region Instance

        protected static Object _instanceSyncObj = new Object();
        protected static WorkflowRuntimeManager _instance;


        public static WorkflowRuntimeManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_instanceSyncObj)
                    {
                        if (_instance == null)
                        {
                            _instance = new WorkflowRuntimeManager();
                        }
                    }
                }
                return _instance;
            }
        }

        #endregion

        #region Constructors

        protected WorkflowRuntimeManager()
        {
            _logger = new Log();

            _workflowRuntime = new WorkflowRuntime();

            _workflowRuntime.WorkflowTerminated += OnWorkflowTerminated;

            var persistenceConnectionString = ConfigurationManager.ConnectionStrings["PersistenceConnectionString"];
            if (persistenceConnectionString != null)
            {
                var sqlPersistenceService = new SqlWorkflowPersistenceService(persistenceConnectionString.ConnectionString, true, new TimeSpan(0, 5, 0), new TimeSpan(0, 2, 0));
                sqlPersistenceService.EnableRetries = true;
                _workflowRuntime.AddService(sqlPersistenceService);
            }

            var trackingConnectionString = ConfigurationManager.ConnectionStrings["TrackingConnectionString"];
            if (trackingConnectionString != null)
            {
                var trackingService = new SqlTrackingService(trackingConnectionString.ConnectionString);
                _workflowRuntime.AddService(trackingService);
            }

            _workflowRuntime.AddService(_logger);

            _workflowRuntime.StartRuntime();
        }

        #endregion

        #region EventHandlers

        protected void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
        {
            var errorMessage = String.Format("Рабочий поток с идентификатором '{0}' был прерван.{1}Причина:{1}{2}", e.WorkflowInstance.InstanceId, Environment.NewLine, ExceptionHelper.GetFullInformation(e.Exception));
            _logger.Error(errorMessage, e.Exception);
        }

        #endregion

        #region Public Methods For Internal Use

        /// <summary>
        /// Создает и запускает рабочий поток указанного типа
        /// </summary>
        /// <param name="workflowType">Тип рабочего потока</param>
        /// <returns></returns>
        internal Guid StartWorkflow(Type workflowType)
        {
            // Создаем рабочий поток
            var workflowInstance = _workflowRuntime.CreateWorkflow(workflowType);
            // И запускаем его
            workflowInstance.Start();
            return workflowInstance.InstanceId;
        }

        /// <summary>
        /// Помещает данные в очередь, связанную с рабочим потоком
        /// </summary>
        /// <param name="workflowInstanceId">Идентификатор рабочего потока</param>
        /// <param name="queueName">Импя очереди</param>
        /// <param name="data">Данные</param>
        internal void PutIntoQueue(Guid workflowInstanceId, IComparable queueName, Object data)
        {
            // Получаем рабочий поток по его идентификатору
            var workflowInstance = _workflowRuntime.GetWorkflow(workflowInstanceId);
            // Помещаем данные в очередь queueName.
            // Так как нам уведомления о доставке не требуется - остальные параметры null.
            workflowInstance.EnqueueItem(queueName, data, null, null);
        }


        #endregion

        #region Public Methods

        /// <summary>
        /// Этот метод ничего не делает. Используется для инициализации среды выполнения рабочих потоков
        /// </summary>
        public void InitializeRuntime()
        {

        }

        #endregion

        #region Finalizer

        ~WorkflowRuntimeManager()
        {
            if (_workflowRuntime != null && _workflowRuntime.IsStarted)
            {
                _workflowRuntime.StopRuntime();
            }
        }

        #endregion
    }
}
